/*
 * sender.c
 *
 *  Created on: 2013. 10. 16.
 *      Author: jphong
 */

#include "np.h"
#include "packet.h"

typedef enum {
	Wait_for_call,
	Wait_for_ACK,
	Closing
} State;

extern int DEBUG_ON;	// print debug messages if set
static unsigned int	ReTxTimeout = 100;		// default timeout in msec
static char *localhost = "127.0.0.1";			// default receiver hostname
static char *port = "5678";						// default receiver port

//Packet buffers
static Packet 	sndPacket, rcvPacket;
/*
 * rdt3.0 sender
 */
int main( int argc, char **argv )
{
	struct sockaddr_in peer;
	int 	fd, s;
	unsigned char sndSeq;
	Event 	event;
	State 	state;
	Packet	*sndpkt = &sndPacket;
	Packet *rcvpkt = &rcvPacket;
	char	*data = &(sndPacket.data[0]);
	char	*hostname, *service;
	int 	opt;

	// Command line option processing
	while ( (opt = getopt(argc, argv, "dt:")) != -1) {
		switch (opt) {
		case 'd':
			DEBUG_ON = 1; 	// Debug mode
			break;
		case 't':
			ReTxTimeout = atoi(optarg);
			break;
		default:
			error(1, 0, "Usage: %s [-d] [-t timeout] [<receiver name or IP address> <port number>]\n", argv[0] );
		}
	}
	if (optind == argc-2) {
		hostname = argv[optind];
		service = argv[optind+1];
	} else if (optind == argc) {
		hostname = localhost;
		service = port;
	} else
		error(1, 0, "Usage: %s [-d] [-t timeout] [<receiver name or IP address> <port number>]\n", argv[0] );
	debug("To %s:%s\tTimeout = %dmsec\tDebugMode = %s\n", hostname, service, ReTxTimeout,
			DEBUG_ON ? "on" : "off");

	set_address( hostname, service, &peer, "udp" ); // Set peer's IP and port

	if ( (s = socket( AF_INET, SOCK_DGRAM, 0 )) < 0)
		error(1, errno, "socket creation failed\n" );

	fd = fileno(stdin);

	// Init conditiion
	sndSeq = 0;
	state = Wait_for_call;

	// FSM
	for (;;) {
		int 	datalen, rcvlen;

		event = getEvent(fd, s);
		// Actions taken by State and Event
		switch (state) {
		// Wait_for_call state
		case Wait_for_call:
			switch (event) {
			case RDT_SEND:
				if ( fgets( data, MAX_DATA, stdin) != NULL ) {  // Get data from above

					memset(&dataPacket, 0, sizeof(dataPacket)); //memory 0 set
					read_byte = read(0, dataPacket.data, MAX_DATA); //readline
					debug("read seq = %d", s_seq);


					continue;
				} else {				// EoF encountered. Close the session

					if(read_byte > 0){
						make_pkt(&dataPacket, s_seq, read_byte);
						udt_send(s, &dataPacketm sizeof(dataPacket));

						debug("[DATA] send Packet: type[%d] seq[%d] len[%d] checksum[%x]\n", dataPacket.header.type, s_seq, dataPacket.header.checksum);

						s_seq++;
						start_timer(TIMEOUT);
						state = 1;
					}
					else if(read_byte == 0){
						memset(&controlPacket, 0, sizeof(controlPacket)); //memory 0 set
						make_cpkt(&controlPacket, END, s_seq);
						udt_send(s, &controlPacket, sizeof(controlPacket));

						debug("[END] send Packet: type[%d] seq[%d] len[%d] checksum[%x]\n",
								controlPacket.header.type, s_seqm controlPacket.header.len,
								controlPacket.header.checksum);

						end=1;
						s_seq++;
						start_timer(TIMEOUT);
						state = 1;
					}

					else if(read_byte < 0)
						error(1, errno, "Data Read Fault");

				}


			default:
				continue;				// ignore the event
			}
		//Wait_for_ACK state
		case Wait_for_ACK:
			switch (event) {
			case RDT_RCV:
				read_byte = recvfrom(s,&controlPacket, 8, 0, (struct sockaddr *)&peer, &clnt_addr_size);

				check = checkCPacket(&controlPacket, s_seq);

				if(check == ACK){
					stop_timer();
					debug("[ACK] rcv Packet : type[%d] seq[%d] len[%d] checksum[%d] \n",
							controlPacket.header.type, s_seqm controlPacket.header.len,
							controlPacket.header.checksum);
					state = 0;

				}

				else if(check == END){
					stop_timer();
					debug("[END] rcv Packet : type[%d] seq[%d] len[%d] checksum[%d] \n",
							controlPacket.header.type, s_seqm controlPacket.header.len,
							controlPacket.header.checksum);
					debug("------END-----\n");
					return 0;
				}

				else if(check == FALSE){
					debug("packet error\n");
				}
					continue;
			case TIMEOUT:
				debug("timeout\n");

				if(end == 0){
					debug("[ACK] send Packet : type[%d] seq[%d] len[%d] checksum[%d] \n",
							controlPacket.header.type, s_seqm controlPacket.header.len,
							controlPacket.header.checksum);
					udt_send(s, &dataPacket, sizeof(dataPacket));
					start_timer(TIMEOUT);
				}

				else{
					debug("[END] send Packet : type[%d] seq[%d] len[%d] checksum[%d] \n",
							controlPacket.header.type, s_seqm controlPacket.header.len,
							controlPacket.header.checksum);
					memset(&controlPacket, 0, sizeof(conrtolPacket)); //memory 0 set
					make_cpkt( &controlPacket, END, s_seq);
					udt_send(s, &controlPacket, sizeof(controlPacket));
					start_timer(TIMEOUT);

				}



				continue;
			default:
				continue;
			}
		// Closing state
			// wait an ACK. Sender shall stop when anything hear from receiver
		case Closing:
			switch (event) {
			case RDT_RCV:
				read_byte = recvfrom(s,&controlPacket, 8, 0, (struct sockaddr *)&peer, &clnt_addr_size);

				check = checkCPacket(&controlPacket, s_seq);

				if(check == ACK){
					stop_timer();
					debug("[ACK] rcv Packet : type[%d] seq[%d] len[%d] checksum[%d] \n",  controlPacket.header.type, s_seqm controlPacket.header.len, controlPacket.header.checksum);
					state = 0;
			case TIMEOUT:
				debug("timeout\n");

				if(end == 0){
					debug("[ACK] send Packet : type[%d] seq[%d] len[%d] checksum[%d] \n",  controlPacket.header.type, s_seqm controlPacket.header.len, controlPacket.header.checksum);
					udt_send(s, &dataPacket, sizeof(dataPacket));
					start_timer(TIMEOUT);
				}

				else{
					debug("[END] send Packet : type[%d] seq[%d] len[%d] checksum[%d] \n",  controlPacket.header.type, s_seqm controlPacket.header.len, controlPacket.header.checksum);
					memset(&controlPacket, 0, sizeof(conrtolPacket)); //memory 0 set
					make_cpkt( &controlPacket, END, s_seq);
					udt_send(s, &controlPacket, sizeof(controlPacket));
					start_timer(TIMEOUT);

				}
				continue;
			default:
				continue;
			}	// End of inner switch
		}	// End of outer switch

	}   // End of for loop

}	// End of main

